<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>（Vue源码三）Vue 丰富的选项合并策略 | Jeffery Blog</title><meta name="keywords" content="vue, vue 源码"><meta name="author" content="志非"><meta name="copyright" content="志非"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="vue, vue 源码，Vue 源码目录及构建过程分析">
<meta property="og:type" content="article">
<meta property="og:title" content="（Vue源码三）Vue 丰富的选项合并策略">
<meta property="og:url" content="https://fif.gitee.io/blog/2021/04/23/vue/vue-code-5.mergeOptions/index.html">
<meta property="og:site_name" content="Jeffery Blog">
<meta property="og:description" content="vue, vue 源码，Vue 源码目录及构建过程分析">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://gitee.com/FIF/pic-beg/raw/master/images/vue/vue-highcharts_1200.webp">
<meta property="article:published_time" content="2021-04-22T16:00:00.000Z">
<meta property="article:modified_time" content="2021-04-13T16:00:00.000Z">
<meta property="article:author" content="志非">
<meta property="article:tag" content="vue">
<meta property="article:tag" content="vue 源码">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://gitee.com/FIF/pic-beg/raw/master/images/vue/vue-highcharts_1200.webp"><link rel="shortcut icon" href="/blog/img/home/avatar.jpeg"><link rel="canonical" href="https://fif.gitee.io/blog/2021/04/23/vue/vue-code-5.mergeOptions/"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//fonts.googleapis.com" crossorigin=""/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/blog/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/instantsearch.js@2.10.5/dist/instantsearch.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/instantsearch.js@2.10.5/dist/instantsearch.min.js" defer></script><script async="async" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script><script>(adsbygoogle = window.adsbygoogle || []).push({
  google_ad_client: '',
  enable_page_level_ads: 'true'
});</script><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Titillium+Web&amp;display=swap" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = { 
  root: '/blog/',
  algolia: {"appId":"QYKLQ4BSGP","apiKey":"d45fc66694b4260b20711a30fdb5c4e1","indexName":"jeffery-blog","hits":{"per_page":6},"languages":{"input_placeholder":"搜索文章","hits_empty":"找不到您查询的内容：${query}","hits_stats":"找到 ${hits} 条结果，用时 ${time} 毫秒"}},
  localSearch: undefined,
  translate: {"defaultEncoding":2,"translateDelay":0,"msgToTraditionalChinese":"繁","msgToSimplifiedChinese":"簡"},
  noticeOutdate: {"limitDay":200,"position":"top","messagePrev":"本文修改于","messageNext":"天前，如果本文有一定的时效性，那么本文可能已经失效~"},
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: true,
    post: false
  },
  runtime: '天',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'mediumZoom',
  Snackbar: {"chs_to_cht":"你已切换为繁体","cht_to_chs":"你已切换为简体","day_to_night":"你已切换为深色模式","night_to_day":"你已切换为浅色模式","bgLight":"#49b1f5","bgDark":"#121212","position":"bottom-left"},
  source: {
    jQuery: 'https://cdn.jsdelivr.net/npm/jquery@latest/dist/jquery.min.js',
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/justifiedGallery/dist/js/jquery.justifiedGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/justifiedGallery/dist/css/justifiedGallery.min.css'
    },
    fancybox: {
      js: 'https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.js',
      css: 'https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@latest/dist/jquery.fancybox.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: true,
  isanchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = { 
  isPost: true,
  isHome: false,
  isHighlightShrink: true,
  isToc: true,
  postUpdate: '2021-04-14 00:00:00'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    })(window)</script><meta name="generator" content="Hexo 5.4.0"><link rel="alternate" href="/blog/atom.xml" title="Jeffery Blog" type="application/atom+xml">
</head><body><div id="loading-box"><div class="loading-left-bg"></div><div class="loading-right-bg"></div><img class="load-image" src= "" data-lazy-src="/blog/img/loading.gif" alt=""/></div><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="author-avatar"><img class="avatar-img" src= "" data-lazy-src="/blog/img/home/avatar.jpeg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="site-data"><div class="data-item is-center"><div class="data-item-link"><a href="/blog/archives/"><div class="headline">文章</div><div class="length-num">12</div></a></div></div><div class="data-item is-center"><div class="data-item-link"><a href="/blog/tags/"><div class="headline">标签</div><div class="length-num">6</div></a></div></div><div class="data-item is-center"><div class="data-item-link"><a href="/blog/categories/"><div class="headline">分类</div><div class="length-num">7</div></a></div></div></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/blog/"><i class="fa-fw fa fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/blog/archives/"><i class="fa-fw fa fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/blog/tags/"><i class="fa-fw fa fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/blog/categories/"><i class="fa-fw fa fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 媒体</span><i class="fas fa-chevron-down expand"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/blog/books/"><i class="fa-fw fas fa-book"></i><span> 阅读</span></a></li><li><a class="site-page child" href="/blog/music/"><i class="fa-fw fa fa-music"></i><span> 音乐</span></a></li><li><a class="site-page child" href="/blog/movies/"><i class="fa-fw fa fa-film"></i><span> 电影</span></a></li><li><a class="site-page child" href="/blog/gallery/"><i class="fa-fw far fa-image"></i><span> 相册</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/blog/box/"><i class="fa-fw fab fa-xbox"></i><span> 聚宝盒</span></a></div><div class="menus_item"><a class="site-page" href="/blog/talk/"><i class="fa-fw far fa-comment"></i><span> 微语</span></a></div><div class="menus_item"><a class="site-page" href="/blog/link/"><i class="fa-fw fa fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/blog/about/"><i class="fa-fw fa fa-heart"></i><span> 关于</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('https://gitee.com/FIF/pic-beg/raw/master/images/vue/vue-highcharts_1200.webp')"><nav id="nav"><span id="blog_name"><a id="site-name" href="/blog/">Jeffery Blog</a></span><div id="menus"><div id="search-button"><a class="site-page social-icon search"><i class="fas fa-search fa-fw"></i><span> 搜索</span></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/blog/"><i class="fa-fw fa fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/blog/archives/"><i class="fa-fw fa fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/blog/tags/"><i class="fa-fw fa fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/blog/categories/"><i class="fa-fw fa fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="javascript:void(0);"><i class="fa-fw fas fa-list"></i><span> 媒体</span><i class="fas fa-chevron-down expand"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/blog/books/"><i class="fa-fw fas fa-book"></i><span> 阅读</span></a></li><li><a class="site-page child" href="/blog/music/"><i class="fa-fw fa fa-music"></i><span> 音乐</span></a></li><li><a class="site-page child" href="/blog/movies/"><i class="fa-fw fa fa-film"></i><span> 电影</span></a></li><li><a class="site-page child" href="/blog/gallery/"><i class="fa-fw far fa-image"></i><span> 相册</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/blog/box/"><i class="fa-fw fab fa-xbox"></i><span> 聚宝盒</span></a></div><div class="menus_item"><a class="site-page" href="/blog/talk/"><i class="fa-fw far fa-comment"></i><span> 微语</span></a></div><div class="menus_item"><a class="site-page" href="/blog/link/"><i class="fa-fw fa fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/blog/about/"><i class="fa-fw fa fa-heart"></i><span> 关于</span></a></div></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">（Vue源码三）Vue 丰富的选项合并策略</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2021-04-22T16:00:00.000Z" title="发表于 2021-04-23 00:00:00">2021-04-23</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2021-04-13T16:00:00.000Z" title="更新于 2021-04-14 00:00:00">2021-04-14</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/blog/categories/%E5%89%8D%E7%AB%AF/">前端</a><i class="fas fa-angle-right post-meta-separator"></i><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/blog/categories/%E5%89%8D%E7%AB%AF/vue/">vue</a><i class="fas fa-angle-right post-meta-separator"></i><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/blog/categories/%E5%89%8D%E7%AB%AF/vue/vue-%E6%BA%90%E7%A0%81/">vue 源码</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">4.4k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>18分钟</span></span><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="（Vue源码三）Vue 丰富的选项合并策略"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"></span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>选项合并在执行 <code>Vue</code> 实例化、<code>Vue.mixin</code>、<code>Vue.extend</code>、组件内部定义 <code>extends</code> 、<code>mixins</code> 等场景使用，目的实现功能的扩展、复用。</p>
<h2 id="选项合并使用场景"><a href="#选项合并使用场景" class="headerlink" title="选项合并使用场景"></a>选项合并使用场景</h2><h3 id="init-初始化合并选项"><a href="#init-初始化合并选项" class="headerlink" title="_init 初始化合并选项"></a>_init 初始化合并选项</h3><p><code>new Vue</code> 执行 <code>_init</code> 方法进行选项合并</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">Vue.prototype._init = <span class="function"><span class="keyword">function</span> (<span class="params">options</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  <span class="keyword">if</span> (options &amp;&amp; options._isComponent) &#123; <span class="comment">// 初始化内部子组件</span></span><br><span class="line">    initInternalComponent(vm, options)</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// 根组件选项合并，将合并后的选项赋值给实例的 $options 属性</span></span><br><span class="line">    vm.$options = mergeOptions(</span><br><span class="line">      resolveConstructorOptions(vm.constructor), <span class="comment">// 返回 Vue 构造函数自身的配置项</span></span><br><span class="line">      options || &#123;&#125;,</span><br><span class="line">      vm</span><br><span class="line">    )</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="resolveConstructorOptions-选项合并"><a href="#resolveConstructorOptions-选项合并" class="headerlink" title="resolveConstructorOptions 选项合并"></a><code>resolveConstructorOptions</code> 选项合并</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">resolveConstructorOptions</span> (<span class="params">Ctor: Class&lt;Component&gt;</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> options = Ctor.options</span><br><span class="line">  <span class="keyword">if</span> (Ctor.super) &#123; <span class="comment">// super 属性在 Vue.extend 会赋值</span></span><br><span class="line">    <span class="comment">// ....</span></span><br><span class="line">      options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions) <span class="comment">// 处理父组件 options 该修改后</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> options</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="Vue-extend-注册组件，选项合并"><a href="#Vue-extend-注册组件，选项合并" class="headerlink" title="Vue.extend 注册组件，选项合并"></a><code>Vue.extend</code> 注册组件，选项合并</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">Vue.extend = <span class="function"><span class="keyword">function</span> (<span class="params">extendOptions: <span class="built_in">Object</span></span>): <span class="title">Function</span> </span>&#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">   </span><br><span class="line">    <span class="comment">// 子类和父类构造器的配置选项进行合并</span></span><br><span class="line">    Sub.options = mergeOptions(</span><br><span class="line">      Super.options,</span><br><span class="line">      extendOptions</span><br><span class="line">    )</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="Vue-mixin-选项合并"><a href="#Vue-mixin-选项合并" class="headerlink" title="Vue.mixin 选项合并"></a><code>Vue.mixin</code> 选项合并</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Vue.mixin = <span class="function"><span class="keyword">function</span> (<span class="params">mixin: <span class="built_in">Object</span></span>) </span>&#123;</span><br><span class="line">  <span class="built_in">this</span>.options = mergeOptions(<span class="built_in">this</span>.options, mixin)</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">this</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="mergeOptions-选项合并流程"><a href="#mergeOptions-选项合并流程" class="headerlink" title="mergeOptions 选项合并流程"></a>mergeOptions 选项合并流程</h2><p><code>mergeOptions</code> 在选项合并之前，会进行组件名称的检验、规范化 <code>props</code>, <code>inject</code> 和 <code>directives</code></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">mergeOptions</span> (<span class="params">parent,child,vm</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 组件名称的检验</span></span><br><span class="line">    <span class="keyword">if</span> (process.env.NODE_ENV !== <span class="string">&#x27;production&#x27;</span>) &#123;</span><br><span class="line">      checkComponents(child) <span class="comment">// components规范检验</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> child === <span class="string">&#x27;function&#x27;</span>) &#123;</span><br><span class="line">      child = child.options;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// props,inject,directives的校验和规范化</span></span><br><span class="line">    normalizeProps(child, vm); <span class="comment">// 将 props 数组转化为带 type 属性对象</span></span><br><span class="line">    normalizeInject(child, vm); <span class="comment">// 将 inject 数组转化为带 from 属性对象</span></span><br><span class="line">    normalizeDirectives(child); <span class="comment">// 将 directives 函数转化为带 bind, update 钩子函数</span></span><br><span class="line">    <span class="comment">// 针对extends, mixins 扩展的子类构造器，递归调用进行选项合并</span></span><br><span class="line">    <span class="keyword">if</span> (!child._base) &#123;</span><br><span class="line">      <span class="comment">// extends</span></span><br><span class="line">      <span class="keyword">if</span> (child.extends) &#123;</span><br><span class="line">        parent = mergeOptions(parent, child.extends, vm);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="comment">// mixins</span></span><br><span class="line">      <span class="keyword">if</span> (child.mixins) &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>, l = child.mixins.length; i &lt; l; i++) &#123;</span><br><span class="line">          parent = mergeOptions(parent, child.mixins[i], vm);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">var</span> options = &#123;&#125;;</span><br><span class="line">    <span class="keyword">var</span> key;</span><br><span class="line">    <span class="keyword">for</span> (key <span class="keyword">in</span> parent) &#123;</span><br><span class="line">      mergeField(key);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (key <span class="keyword">in</span> child) &#123;</span><br><span class="line">      <span class="keyword">if</span> (!hasOwn(parent, key)) &#123;</span><br><span class="line">        mergeField(key);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">mergeField</span> (<span class="params">key</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// 拿到各个选择指定的选项配置，如果没有则用默认的配置</span></span><br><span class="line">      <span class="keyword">var</span> strat = strats[key] || defaultStrat;</span><br><span class="line">      <span class="comment">// 执行各自的合并策略</span></span><br><span class="line">      options[key] = strat(parent[key], child[key], vm, key);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// console.log(options)</span></span><br><span class="line">    <span class="keyword">return</span> options</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>

<h3 id="components-规范检验"><a href="#components-规范检验" class="headerlink" title="components 规范检验"></a>components 规范检验</h3><p> 组件名称 <code>components</code> 由于写法比较随意，所以组件命名需要遵守很多规范，比如组件名首字符必须是字母，组件名称由字母、数字、<code>.</code>、<code>_</code>等组成，不能包含非法字符，不能用原生组件名 <code>component</code>, <code>slot</code>，不能用 <code>html</code> 保留的标签(如：h1, svg )。这些都会在 <code>validateComponentName</code> 函数做校验。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// components规范检查函数</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">checkComponents</span> (<span class="params">options</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 遍历components对象，对每个属性值校验。</span></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">var</span> key <span class="keyword">in</span> options.components) &#123;</span><br><span class="line">    validateComponentName(key);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">validateComponentName</span> (<span class="params">name</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (!<span class="keyword">new</span> <span class="built_in">RegExp</span>((<span class="string">&quot;^[a-zA-Z][\\-\\.0-9_&quot;</span> + (unicodeRegExp.source) + <span class="string">&quot;]*$&quot;</span>)).test(name)) &#123;</span><br><span class="line">    <span class="comment">// 正则判断检测是否为非法的标签，例如数字开头</span></span><br><span class="line">    warn(</span><br><span class="line">      <span class="string">&#x27;Invalid component name: &quot;&#x27;</span> + name + <span class="string">&#x27;&quot;. Component names &#x27;</span> +</span><br><span class="line">      <span class="string">&#x27;should conform to valid custom element name in html5 specification.&#x27;</span></span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 不能使用Vue自身自定义的组件名，如slot, component,不能使用html的保留标签，如 h1, svg 等</span></span><br><span class="line">  <span class="keyword">if</span> (isBuiltInTag(name) || config.isReservedTag(name)) &#123;</span><br><span class="line">    warn(</span><br><span class="line">      <span class="string">&#x27;Do not use built-in or reserved HTML elements as component &#x27;</span> +</span><br><span class="line">      <span class="string">&#x27;id: &#x27;</span> + name</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="props-规范"><a href="#props-规范" class="headerlink" title="props 规范"></a>props 规范</h3><p><code>props</code> 选项的书写形式有两种，分别是</p>
<ul>
<li>数组形式 <code>&#123; props: [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] &#125;</code></li>
<li>带校验规则的对象形式 <code>&#123; props: &#123; a: &#123; type: &#39;String&#39;, default: &#39;prop校验&#39; &#125; &#125;&#125;</code> 从源码上看，两种形式最终都会转换成对象的形式。</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// props规范</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">normalizeProps</span> (<span class="params">options, vm</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> props = options.props;</span><br><span class="line">    <span class="keyword">if</span> (!props) &#123; <span class="keyword">return</span> &#125;</span><br><span class="line">    <span class="keyword">var</span> res = &#123;&#125;;</span><br><span class="line">    <span class="keyword">var</span> i, val, name;</span><br><span class="line">    <span class="comment">// props选项数据有两种形式，一种是[&#x27;a&#x27;, &#x27;b&#x27;, &#x27;c&#x27;],一种是&#123; a: &#123; type: &#x27;String&#x27;, default: &#x27;hahah&#x27; &#125;&#125;</span></span><br><span class="line">    <span class="comment">// 数组</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">Array</span>.isArray(props)) &#123;</span><br><span class="line">      i = props.length;</span><br><span class="line">      <span class="keyword">while</span> (i--) &#123;</span><br><span class="line">        val = props[i];</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> val === <span class="string">&#x27;string&#x27;</span>) &#123;</span><br><span class="line">          <span class="comment">// a-b 会转化为 aB</span></span><br><span class="line">          name = camelize(val);</span><br><span class="line">          <span class="comment">// 默认将数组形式的props转换为对象形式。</span></span><br><span class="line">          res[name] = &#123; <span class="attr">type</span>: <span class="literal">null</span> &#125;; </span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">          <span class="comment">// 规则：保证是字符串</span></span><br><span class="line">          warn(<span class="string">&#x27;props must be strings when using array syntax.&#x27;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (isPlainObject(props)) &#123;</span><br><span class="line">      <span class="keyword">for</span> (<span class="keyword">var</span> key <span class="keyword">in</span> props) &#123;</span><br><span class="line">        val = props[key];</span><br><span class="line">        name = camelize(key);</span><br><span class="line">        res[name] = isPlainObject(val)</span><br><span class="line">          ? val</span><br><span class="line">          : &#123; <span class="attr">type</span>: val &#125;; <span class="comment">// val 为 Number, String, Boolean</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="comment">// 非数组，非对象则判定props选项传递非法</span></span><br><span class="line">      warn(</span><br><span class="line">        <span class="string">&quot;Invalid value for option \&quot;props\&quot;: expected an Array or an Object, &quot;</span> +</span><br><span class="line">        <span class="string">&quot;but got &quot;</span> + (toRawType(props)) + <span class="string">&quot;.&quot;</span>,</span><br><span class="line">        vm</span><br><span class="line">      );</span><br><span class="line">    &#125;</span><br><span class="line">    options.props = res;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>

<h3 id="inject-的规范"><a href="#inject-的规范" class="headerlink" title="inject 的规范"></a>inject 的规范</h3><p><code>inject</code> 的用法和 <code>props</code> 的用法一样，有数组和对象两种方式</p>
<p>基本的使用如下：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 父组件</span></span><br><span class="line"><span class="keyword">var</span> Provider = &#123;</span><br><span class="line">  provide: &#123;</span><br><span class="line">    foo: <span class="string">&#x27;bar&#x27;</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 后代组件</span></span><br><span class="line"><span class="keyword">var</span> Child = &#123;</span><br><span class="line">  <span class="comment">// 数组写法</span></span><br><span class="line">  inject: [<span class="string">&#x27;foo&#x27;</span>],</span><br><span class="line">  <span class="comment">// 对象写法</span></span><br><span class="line">  inject: &#123;</span><br><span class="line">    foo: &#123;</span><br><span class="line">      <span class="keyword">from</span>: <span class="string">&#x27;foo&#x27;</span>,</span><br><span class="line">      <span class="keyword">default</span>: <span class="string">&#x27;bardefault&#x27;</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>inject</code> 源码实现和 <code>props</code> 的校验规则一致，最终 <code>inject</code> 都会转换为带 <code>from</code> 对象的形式存在。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// inject的规范化</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">normalizeInject</span> (<span class="params">options, vm</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> inject = options.inject;</span><br><span class="line">    <span class="keyword">if</span> (!inject) &#123; <span class="keyword">return</span> &#125;</span><br><span class="line">    <span class="keyword">var</span> normalized = options.inject = &#123;&#125;;</span><br><span class="line">    <span class="comment">//数组的形式</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">Array</span>.isArray(inject)) &#123;</span><br><span class="line">      <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; inject.length; i++) &#123;</span><br><span class="line">        <span class="comment">// from: 属性是在可用的注入内容中搜索用的 key (字符串或 Symbol)</span></span><br><span class="line">        normalized[inject[i]] = &#123; <span class="attr">from</span>: inject[i] &#125;;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (isPlainObject(inject)) &#123;</span><br><span class="line">      <span class="comment">// 对象的处理</span></span><br><span class="line">      <span class="keyword">for</span> (<span class="keyword">var</span> key <span class="keyword">in</span> inject) &#123;</span><br><span class="line">        <span class="keyword">var</span> val = inject[key];</span><br><span class="line">        normalized[key] = isPlainObject(val)</span><br><span class="line">          ? extend(&#123; <span class="attr">from</span>: key &#125;, val)</span><br><span class="line">          : &#123; <span class="attr">from</span>: val &#125;;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="comment">// 非法规则</span></span><br><span class="line">      warn(</span><br><span class="line">        <span class="string">&quot;Invalid value for option \&quot;inject\&quot;: expected an Array or an Object, &quot;</span> +</span><br><span class="line">        <span class="string">&quot;but got &quot;</span> + (toRawType(inject)) + <span class="string">&quot;.&quot;</span>,</span><br><span class="line">        vm</span><br><span class="line">      );</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>

<h3 id="directives-的规范"><a href="#directives-的规范" class="headerlink" title="directives 的规范"></a>directives 的规范</h3><p><code>Vue</code> 自定义指令提供了五个钩子函数 <code>bind, inserted, update, componentUpdated, unbind</code>, 除此之外，自定义指令还提供了函数的写法</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  directives: &#123;</span><br><span class="line">    <span class="string">&#x27;color-swatch&#x27;</span>: <span class="function"><span class="keyword">function</span>(<span class="params">el, binding</span>) </span>&#123;</span><br><span class="line">        el.style.backgroundColor = binding.value</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>vue</code> 源码实现，会规范化指令函数写法，将函数赋予 <code>bind,update</code> 钩子</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">normalizeDirectives</span> (<span class="params">options: <span class="built_in">Object</span></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> dirs = options.directives</span><br><span class="line">  <span class="keyword">if</span> (dirs) &#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">const</span> key <span class="keyword">in</span> dirs) &#123;</span><br><span class="line">      <span class="keyword">const</span> def = dirs[key]</span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">typeof</span> def === <span class="string">&#x27;function&#x27;</span>) &#123;</span><br><span class="line">        dirs[key] = &#123; <span class="attr">bind</span>: def, <span class="attr">update</span>: def &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="Vue-的-extends-和-mixins-扩展"><a href="#Vue-的-extends-和-mixins-扩展" class="headerlink" title="Vue 的 extends 和 mixins 扩展"></a>Vue 的 extends 和 mixins 扩展</h3><p>校验规范后，会针对 <code>extends</code> 和 <code>mixins</code> 选项进行递归处理</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (!child._base) &#123;</span><br><span class="line">  <span class="comment">// extends</span></span><br><span class="line">  <span class="keyword">if</span> (child.extends) &#123;</span><br><span class="line">    parent = mergeOptions(parent, child.extends, vm);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// mixins</span></span><br><span class="line">  <span class="keyword">if</span> (child.mixins) &#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>, l = child.mixins.length; i &lt; l; i++) &#123;</span><br><span class="line">      parent = mergeOptions(parent, child.mixins[i], vm);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="选项合并策略"><a href="#选项合并策略" class="headerlink" title="选项合并策略"></a>选项合并策略</h2><p>遍历 <code>parent</code> 和 <code>child</code> 调用 <code>mergeField</code> 方法，匹配定义的合并策略 <code>strats</code> ，不匹配则使用默认合并策略 <code>defaultStrat</code>, <code>vuex</code>、<code>vue-router</code>、<code>$axios</code> 等插件使用的是默认选项合并策略</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (key <span class="keyword">in</span> parent) &#123;</span><br><span class="line">  mergeField(key)</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">for</span> (key <span class="keyword">in</span> child) &#123;</span><br><span class="line">  <span class="keyword">if</span> (!hasOwn(parent, key)) &#123;</span><br><span class="line">    mergeField(key)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">mergeField</span> (<span class="params">key</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 拿到各个选择指定的选项配置，如果没有则用默认的配置</span></span><br><span class="line">  <span class="comment">// strats下每个key对应的便是每个特殊选项的合并策略</span></span><br><span class="line">  <span class="keyword">const</span> strat = strats[key] || defaultStrat</span><br><span class="line">  <span class="comment">// 执行各自的合并策略</span></span><br><span class="line">  options[key] = strat(parent[key], child[key], vm, key)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><code>Vue</code> 的合并策略主要分为:</p>
<ul>
<li> <code>data</code> 合并策略</li>
<li> <code>watch</code> 合并策略 ,</li>
<li>生命周期钩子合并策略</li>
<li><code>components, directives, filters</code> 资源合并策略</li>
<li><code>computed, inject, props, methods </code>合并策略</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// vue 定义的合并策略对象</span></span><br><span class="line"><span class="keyword">const</span> strats = config.optionMergeStrategies</span><br><span class="line"></span><br><span class="line"><span class="comment">// data</span></span><br><span class="line">strats.data = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123; ... &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// hook 生命周期</span></span><br><span class="line">LIFECYCLE_HOOKS.forEach(<span class="function"><span class="params">hook</span> =&gt;</span> &#123;</span><br><span class="line">  strats[hook] = mergeHook</span><br><span class="line">&#125;)</span><br><span class="line">  </span><br><span class="line"><span class="comment">// 资源 components, directives, filters</span></span><br><span class="line">ASSET_TYPES.forEach(<span class="function"><span class="keyword">function</span> (<span class="params">type</span>) </span>&#123;</span><br><span class="line">  strats[type + <span class="string">&#x27;s&#x27;</span>] = mergeAssets</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// watch</span></span><br><span class="line">strats.watch =<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;...&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// computed, inject, props, methods</span></span><br><span class="line">strats.props =</span><br><span class="line">strats.methods =</span><br><span class="line">strats.inject =</span><br><span class="line">strats.computed = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123; ... &#125;</span><br></pre></td></tr></table></figure>



<p>在 <code>Vue</code> 的官网也说到 <code>vue</code> 可以使用全局配置 <code>config.optionMergeStrategies</code> 自定义合并策略，其实从上面源码也知道就是在<code>optionMergeStrategies</code>对象上添加函数，在访问 <code>strats</code> 可以拿到合并函数</p>
<h3 id="data-合并策略"><a href="#data-合并策略" class="headerlink" title="data 合并策略"></a>data 合并策略</h3><p><code>data</code> 可以在 <code>vue</code> 创建实例时传递的是一个对象，但是在组件内部定义时必须传递的是函数</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// data的合并</span></span><br><span class="line">strats.data = <span class="function"><span class="keyword">function</span> (<span class="params">parentVal, childVal, vm</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// vm代表是否为Vue创建的实例，否则是子父类的关系</span></span><br><span class="line">  <span class="keyword">if</span> (!vm) &#123;</span><br><span class="line">    <span class="comment">// 必须保证子类的 data 类型是一个函数而不是一个对象</span></span><br><span class="line">    <span class="keyword">if</span> (childVal &amp;&amp; <span class="keyword">typeof</span> childVal !== <span class="string">&#x27;function&#x27;</span>) &#123; </span><br><span class="line">      warn(<span class="string">&#x27;The &quot;data&quot; option should be a function &#x27;</span> + <span class="string">&#x27;that returns a per-instance value in component &#x27;</span> + <span class="string">&#x27;definitions.&#x27;</span>,vm);</span><br><span class="line">      <span class="keyword">return</span> parentVal</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> mergeDataOrFn(parentVal, childVal)</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> mergeDataOrFn(parentVal, childVal, vm); <span class="comment">// vue 实例时需要传递vm作为函数的第三个参数</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p><code>data</code> 策略最终调用的 <code>mergeDataOrFn</code> 方法，区别在于当前 <code>vm</code> 是否是实例，或者是单纯的子父类的关系。如果是子父类的关系，需要对 <code>data</code> 选项进行规范校验，保证它的类型是一个函数而不是对象。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">mergeDataOrFn</span> (<span class="params"> parentVal, childVal, vm </span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 子父类</span></span><br><span class="line">  <span class="keyword">if</span> (!vm) &#123;</span><br><span class="line">    <span class="keyword">if</span> (!childVal) &#123; <span class="comment">// 子类不存在data选项，则合并结果为父类data选项</span></span><br><span class="line">      <span class="keyword">return</span> parentVal</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (!parentVal) &#123; <span class="comment">// 父类不存在data选项，则合并结果为子类data选项</span></span><br><span class="line">      <span class="keyword">return</span> childVal</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> <span class="title">mergedDataFn</span> (<span class="params"></span>) </span>&#123; <span class="comment">// data选项在父类和子类同时存在的情况下返回的是一个函数</span></span><br><span class="line">      <span class="comment">// 子类实例和父类实例，分别将子类和父类实例中 data 函数执行后返回的对象传递给mergeData函数做数据合并</span></span><br><span class="line">      <span class="keyword">return</span> mergeData(</span><br><span class="line">        <span class="keyword">typeof</span> childVal === <span class="string">&#x27;function&#x27;</span> ? childVal.call(<span class="built_in">this</span>, <span class="built_in">this</span>) : childVal,</span><br><span class="line">        <span class="keyword">typeof</span> parentVal === <span class="string">&#x27;function&#x27;</span> ? parentVal.call(<span class="built_in">this</span>, <span class="built_in">this</span>) : parentVal</span><br><span class="line">      )</span><br><span class="line">    &#125;</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">  <span class="comment">// Vue实例</span></span><br><span class="line">    <span class="comment">// vue构造函数实例对象</span></span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> <span class="title">mergedInstanceDataFn</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">      <span class="keyword">var</span> instanceData = <span class="keyword">typeof</span> childVal === <span class="string">&#x27;function&#x27;</span></span><br><span class="line">        ? childVal.call(vm, vm)</span><br><span class="line">        : childVal;</span><br><span class="line">      <span class="keyword">var</span> defaultData = <span class="keyword">typeof</span> parentVal === <span class="string">&#x27;function&#x27;</span></span><br><span class="line">        ? parentVal.call(vm, vm)</span><br><span class="line">        : parentVal;</span><br><span class="line">      <span class="keyword">if</span> (instanceData) &#123;</span><br><span class="line">        <span class="comment">// 当实例中传递data选项时，将实例的data对象和Vm构造函数上的data属性选项合并</span></span><br><span class="line">        <span class="keyword">return</span> mergeData(instanceData, defaultData)</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 当实例中不传递data时，默认返回Vm构造函数上的data属性选项</span></span><br><span class="line">        <span class="keyword">return</span> defaultData</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>从源码的实现看，<code>data</code> 的合并不是简单的将两个数据对象进行合并，而是直接返回一个 <code>mergedDataFn</code> 或者<code>mergedInstanceDataFn</code> 函数，而真正合并的时机是在后续初始化数据响应式系统调用 <code>initData</code> 的环节进行的，初始化数据响应式系统的第一步就是拿到合并后的数据，也就是执行 <code>mergeData</code> 逻辑。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">mergeData</span> (<span class="params">to: <span class="built_in">Object</span>, <span class="keyword">from</span>: ?<span class="built_in">Object</span></span>): <span class="title">Object</span> </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (!<span class="keyword">from</span>) <span class="keyword">return</span> to</span><br><span class="line">  <span class="keyword">let</span> key, toVal, fromVal</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Reflect.ownKeys可以拿到Symbol属性</span></span><br><span class="line">  <span class="keyword">const</span> keys = hasSymbol</span><br><span class="line">    ? <span class="built_in">Reflect</span>.ownKeys(<span class="keyword">from</span>)</span><br><span class="line">    : <span class="built_in">Object</span>.keys(<span class="keyword">from</span>)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; keys.length; i++) &#123;</span><br><span class="line">    key = keys[i]</span><br><span class="line">    <span class="comment">// in case the object is already observed...</span></span><br><span class="line">    <span class="keyword">if</span> (key === <span class="string">&#x27;__ob__&#x27;</span>) <span class="keyword">continue</span></span><br><span class="line">    toVal = to[key]</span><br><span class="line">    fromVal = <span class="keyword">from</span>[key]</span><br><span class="line">    <span class="keyword">if</span> (!hasOwn(to, key)) &#123;</span><br><span class="line">      <span class="comment">// 子的数据父没有，则将新增的数据加入响应式系统中。</span></span><br><span class="line">      set(to, key, fromVal)</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (</span><br><span class="line">      toVal !== fromVal &amp;&amp;</span><br><span class="line">      isPlainObject(toVal) &amp;&amp;</span><br><span class="line">      isPlainObject(fromVal)</span><br><span class="line">    ) &#123;</span><br><span class="line">        <span class="comment">// 处理深层对象，当合并的数据为多层嵌套对象时，需要递归调用mergeData进行比较合并</span></span><br><span class="line">      mergeData(toVal, fromVal)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> to</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>mergeData</code> 方法的两个参数是父 <code>data</code> 选项和子 <code>data</code> 选项的结果，也就是两个 <code>data</code> 对象，从源码上看数据合并的原则是，将父类的数据整合到子类的数据选项中，如若父类数据和子类数据冲突时，保留子类数据。如果对象有深层嵌套，则需要递归调用 <code>mergeData</code> 进行数据合并。</p>
<p>为什么 <code>Vue</code> 组件的<code>data</code> 是一个函数，而不是一个对象呢？可以通过函数作用域来解释：组件设计的目的是为了复用，每次通过函数创建相当于在一个独立的内存空间中生成一个 <code>data</code> 的副本，这样每个组件之间的数据不会互相影响。</p>
<h3 id="生命周期钩子函数合并策略"><a href="#生命周期钩子函数合并策略" class="headerlink" title="生命周期钩子函数合并策略"></a>生命周期钩子函数合并策略</h3><p><code>Vue</code> 的生命周期钩子函数多达12个，我们在使用 <code>mixin</code>，<code>extends</code> 选项会混入生命周期钩子函数，那么子父组件的生命周期钩子函数是遵循什么样的规则合并</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> LIFECYCLE_HOOKS = [</span><br><span class="line">  <span class="string">&#x27;beforeCreate&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;created&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;beforeMount&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;mounted&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;beforeUpdate&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;updated&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;beforeDestroy&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;destroyed&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;activated&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;deactivated&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;errorCaptured&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;serverPrefetch&#x27;</span></span><br><span class="line">]</span><br><span class="line"></span><br><span class="line">LIFECYCLE_HOOKS.forEach(<span class="function"><span class="params">hook</span> =&gt;</span> &#123;</span><br><span class="line">  strats[hook] = mergeHook <span class="comment">// 对生命周期钩子选项的合并都执行mergeHook策略</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<p><code>mergeHook</code> 是生命周期钩子合并的策略，简单的对代码进行总结，钩子函数的合并原则是：</p>
<ul>
<li>如果子类和父类都拥有相同钩子选项，则将子类选项和父类选项合并。</li>
<li>如果父类不存在钩子选项，子类存在时，则以数组形式返回子类钩子选项。</li>
<li>当子类不存在钩子选项时，则以父类选项返回。</li>
<li>子父合并时，是将子类选项放在数组的末尾，这样在执行钩子时，永远是父类选项优先于子类选项执行，子类的执行结果会覆盖父类。</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 生命周期钩子选项合并策略</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">mergeHook</span> (<span class="params"></span></span></span><br><span class="line"><span class="function"><span class="params">    parentVal,</span></span></span><br><span class="line"><span class="function"><span class="params">    childVal</span></span></span><br><span class="line"><span class="function"><span class="params">  </span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 1.如果子类和父类都拥有钩子选项，则将子类选项和父类选项合并, </span></span><br><span class="line">    <span class="comment">// 2.如果父类不存在钩子选项，子类存在时，则以数组形式返回子类钩子选项，</span></span><br><span class="line">    <span class="comment">// 3.当子类不存在钩子选项时，则以父类选项返回。</span></span><br><span class="line">    <span class="keyword">var</span> res = childVal ? </span><br><span class="line">                parentVal ? </span><br><span class="line">                  parentVal.concat(childVal) </span><br><span class="line">                  : <span class="built_in">Array</span>.isArray(childVal) ? </span><br><span class="line">                  childVal : [childVal] </span><br><span class="line">              : parentVal; </span><br><span class="line">    <span class="keyword">return</span> res</span><br><span class="line">      ? dedupeHooks(res)</span><br><span class="line">      : res</span><br><span class="line">  &#125;</span><br><span class="line"><span class="comment">// 去重，防止多个组件实例钩子选项相互影响</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">dedupeHooks</span> (<span class="params">hooks</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> res = [];</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; hooks.length; i++) &#123;</span><br><span class="line">      <span class="keyword">if</span> (res.indexOf(hooks[i]) === -<span class="number">1</span>) &#123;</span><br><span class="line">        res.push(hooks[i]);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> res</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>



<h3 id="components-filters-directives-资源合并策略"><a href="#components-filters-directives-资源合并策略" class="headerlink" title="components, filters, directives 资源合并策略"></a>components, filters, directives 资源合并策略</h3><p><code>Vue</code> 默认会带几个选项，分别是 <code>components</code> 组件, <code>directives</code> 指令, <code>filters</code> 过滤器, <code>_base</code></p>
<p>无论是根实例，还是父子实例，都需要和系统自带的资源选项进行合并。它的定义如下：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 资源选项</span></span><br><span class="line"><span class="keyword">var</span> ASSET_TYPES = [</span><br><span class="line">  <span class="string">&#x27;component&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;directive&#x27;</span>,</span><br><span class="line">  <span class="string">&#x27;filter&#x27;</span></span><br><span class="line">];</span><br><span class="line"><span class="comment">// 定义资源合并的策略</span></span><br><span class="line">ASSET_TYPES.forEach(<span class="function"><span class="keyword">function</span> (<span class="params">type</span>) </span>&#123;</span><br><span class="line">  strats[type + <span class="string">&#x27;s&#x27;</span>] = mergeAssets; <span class="comment">// 定义默认策略</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>资源选项的合并，首先会创建一个原型指向父类资源选项的空对象，再将子类选项赋值给空对象。</p>
<p>子类创建是继承父类，可以通过访问原型链拿到定义的值，这就是我们全局注册组件、全局注册指令、全局注册过滤器可以在实例化子组件使用的原因。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 资源选项自定义合并策略</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">mergeAssets</span> (<span class="params">parentVal,childVal,vm,key</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> res = <span class="built_in">Object</span>.create(parentVal || <span class="literal">null</span>); <span class="comment">// 创建一个空对象，其原型指向父类的资源选项。</span></span><br><span class="line">  <span class="keyword">if</span> (childVal) &#123;</span><br><span class="line">    assertObjectType(key, childVal, vm); <span class="comment">// components,filters,directives选项必须为对象</span></span><br><span class="line">    <span class="keyword">return</span> extend(res, childVal) <span class="comment">// 子类选项赋值给空对象</span></span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> res</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>结合下面的例子，看具体合并后的结果：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> vm = <span class="keyword">new</span> Vue(&#123;</span><br><span class="line">  components: &#123;</span><br><span class="line">    componentA: &#123;&#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  directives: &#123;</span><br><span class="line">    <span class="string">&#x27;v-boom&#x27;</span>: &#123;&#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br><span class="line"><span class="built_in">console</span>.log(vm.$options.components, vm.$options.directives)</span><br><span class="line"><span class="comment">// 根实例的选项和资源默认选项合并后的结果</span></span><br><span class="line">&#123;</span><br><span class="line">  components: &#123;</span><br><span class="line">    componentA: &#123;&#125;,</span><br><span class="line">    __proto__: &#123;</span><br><span class="line">      KeepAlive: &#123;&#125;</span><br><span class="line">      Transition: &#123;&#125;</span><br><span class="line">      TransitionGroup: &#123;&#125;</span><br><span class="line">    &#125; </span><br><span class="line">  &#125;,</span><br><span class="line">  directives: &#123;</span><br><span class="line">    <span class="string">&#x27;v-boom&#x27;</span>: &#123;&#125;,</span><br><span class="line">    __proto__: &#123;</span><br><span class="line">      <span class="string">&#x27;v-show&#x27;</span>: &#123;&#125;,</span><br><span class="line">      <span class="string">&#x27;v-model&#x27;</span>: &#123;&#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="watch-选项合并策略"><a href="#watch-选项合并策略" class="headerlink" title="watch 选项合并策略"></a>watch 选项合并策略</h3><p>当需要在数据变化时执行异步或者开销较大的操作时，<code>watch</code> 往往是高效的。对于 <code>watch</code> 选项的合并处理，它类似于生命周期钩子，只要父选项有相同的观测字段，则和子的选项合并为数组，在监测字段改变时同时执行父类选项的监听代码。处理方式和生命钩子选项的区别在于，生命周期钩子选项必须是函数，而 <code>watch</code> 选项最终在合并的数组中可以是包含选项的对象，也可以是对应的回调函数，或者方法名。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">strats.watch = <span class="function"><span class="keyword">function</span> (<span class="params">parentVal,childVal,vm,key</span>) </span>&#123;</span><br><span class="line">    <span class="comment">//火狐浏览器在Object的原型上拥有watch方法，这里对这一现象做了兼容</span></span><br><span class="line">    <span class="comment">// var nativeWatch = (&#123;&#125;).watch;</span></span><br><span class="line">    <span class="keyword">if</span> (parentVal === nativeWatch) &#123; parentVal = <span class="literal">undefined</span>; &#125;</span><br><span class="line">    <span class="keyword">if</span> (childVal === nativeWatch) &#123; childVal = <span class="literal">undefined</span>; &#125;</span><br><span class="line">    <span class="comment">// 没有子，则默认用父选项</span></span><br><span class="line">    <span class="keyword">if</span> (!childVal) &#123; <span class="keyword">return</span> <span class="built_in">Object</span>.create(parentVal || <span class="literal">null</span>) &#125;</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="comment">// 保证watch选项是一个对象</span></span><br><span class="line">      assertObjectType(key, childVal, vm);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 没有父则直接用子选项</span></span><br><span class="line">    <span class="keyword">if</span> (!parentVal) &#123; <span class="keyword">return</span> childVal &#125;</span><br><span class="line">    <span class="keyword">var</span> ret = &#123;&#125;;</span><br><span class="line">    extend(ret, parentVal);</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">var</span> key$<span class="number">1</span> <span class="keyword">in</span> childVal) &#123;</span><br><span class="line">      <span class="keyword">var</span> parent = ret[key$<span class="number">1</span>];</span><br><span class="line">      <span class="keyword">var</span> child = childVal[key$<span class="number">1</span>];</span><br><span class="line">      <span class="comment">// 父的选项先转换成数组</span></span><br><span class="line">      <span class="keyword">if</span> (parent &amp;&amp; !<span class="built_in">Array</span>.isArray(parent)) &#123;</span><br><span class="line">        parent = [parent];</span><br><span class="line">      &#125;</span><br><span class="line">      ret[key$<span class="number">1</span>] = parent</span><br><span class="line">        ? parent.concat(child)</span><br><span class="line">        : <span class="built_in">Array</span>.isArray(child) ? child : [child];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ret</span><br><span class="line">  &#125;;</span><br></pre></td></tr></table></figure>

<p>  下面结合具体的例子看合并结果：</p>
  <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> Parent = Vue.extend(&#123;</span><br><span class="line">  watch: &#123;</span><br><span class="line">    <span class="string">&#x27;test&#x27;</span>: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">&#x27;parent change&#x27;</span>)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">var</span> Child = Parent.extend(&#123;</span><br><span class="line">  watch: &#123;</span><br><span class="line">    <span class="string">&#x27;test&#x27;</span>: &#123;</span><br><span class="line">      handler: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;child change&#x27;</span>)</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="function"><span class="title">data</span>(<span class="params"></span>)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">      test: <span class="number">1</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">var</span> vm = <span class="keyword">new</span> Child().$mount(<span class="string">&#x27;#app&#x27;</span>);</span><br><span class="line">vm.test = <span class="number">2</span>;</span><br><span class="line"><span class="comment">// 输出结果</span></span><br><span class="line">parent change</span><br><span class="line">child change</span><br></pre></td></tr></table></figure>

<p>  对于 <code>watch</code> 选项的合并，最终和父类选项合并成数组，并且数组的选项成员，可以是回调函数，选项对象，或者函数名。</p>
<h3 id="props，methods，inject，computed-合并策略"><a href="#props，methods，inject，computed-合并策略" class="headerlink" title="props，methods，inject，computed 合并策略"></a>props，methods，inject，computed 合并策略</h3><p>源码的设计将 <code>props, methods, inject, computed</code> 归结为一类，他们的配置策略一致，简单概括就是，如果父类不存在选项，则返回子类选项，子类父类都存在时，用子类选项去覆盖父类选项。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 其他选项合并策略</span></span><br><span class="line">strats.props =</span><br><span class="line">strats.methods =</span><br><span class="line">strats.inject =</span><br><span class="line">strats.computed = <span class="function"><span class="keyword">function</span> (<span class="params">parentVal,childVal,vm,key</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (childVal &amp;&amp; <span class="string">&quot;development&quot;</span> !== <span class="string">&#x27;production&#x27;</span>) &#123;</span><br><span class="line">    assertObjectType(key, childVal, vm);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">if</span> (!parentVal) &#123; <span class="keyword">return</span> childVal &#125; <span class="comment">// 父类不存在该选项，则返回子类的选项</span></span><br><span class="line">  <span class="keyword">var</span> ret = <span class="built_in">Object</span>.create(<span class="literal">null</span>);</span><br><span class="line">  extend(ret, parentVal); <span class="comment">// </span></span><br><span class="line">  <span class="keyword">if</span> (childVal) &#123; </span><br><span class="line">    <span class="comment">// 子类选项会覆盖父类选项的值</span></span><br><span class="line">    extend(ret, childVal); &#125; </span><br><span class="line">  <span class="keyword">return</span> ret</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h3 id="provide-合并策略"><a href="#provide-合并策略" class="headerlink" title="provide 合并策略"></a>provide 合并策略</h3><p><code>provide</code> 合并策略和 <code>data</code> 合并策略相同</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">strats.provide = mergeDataOrFn</span><br></pre></td></tr></table></figure>

<h3 id="defaultStrat-默认合并策略"><a href="#defaultStrat-默认合并策略" class="headerlink" title="defaultStrat 默认合并策略"></a>defaultStrat 默认合并策略</h3><p>当 <code>vue</code> 没有定义合并策略时，则使用默认合并策略，比如 <code>vuex</code>, <code>vue-router</code> 使用 <code>defaultStrat</code> 合并</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> defaultStrat = <span class="function"><span class="keyword">function</span> (<span class="params">parentVal: any, childVal: any</span>): <span class="title">any</span> </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> childVal === <span class="literal">undefined</span></span><br><span class="line">    ? parentVal</span><br><span class="line">    : childVal</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="mailto:undefined">志非</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://fif.gitee.io/blog/2021/04/23/vue/vue-code-5.mergeOptions/">https://fif.gitee.io/blog/2021/04/23/vue/vue-code-5.mergeOptions/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://fif.gitee.io/blog" target="_blank">Jeffery Blog</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/blog/tags/vue/">vue</a><a class="post-meta__tags" href="/blog/tags/vue-%E6%BA%90%E7%A0%81/">vue 源码</a></div><div class="post_share"><div class="social-share" data-image="https://gitee.com/FIF/pic-beg/raw/master/images/vue/vue-highcharts_1200.webp" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/social-share.js/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/social-share.js/dist/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/blog/2021/04/24/tools/gitlab/index/"><img class="prev-cover" src= "" data-lazy-src="https://gitee.com/FIF/pic-beg/raw/master/images/vue/Hexo-logo.png" onerror="onerror=null;src='/blog/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">Gitlab webhook 配置企业微信机器人</div></div></a></div><div class="next-post pull-right"><a href="/blog/2021/04/17/vue/vue-code-4.debugger/"><img class="next-cover" src= "" data-lazy-src="https://gitee.com/FIF/pic-beg/raw/master/images/vue/1_b2Ig4HR2UOQEV5I2QnQrrw.png" onerror="onerror=null;src='/blog/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">（Vue源码二）Vue实例挂载流程</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span> 相关推荐</span></div><div class="relatedPosts-list"><div><a href="/blog/2021/04/12/vue/vue-code-3.build/" title="（Vue源码一）Vue 源码设计和构建流程"><img class="cover" src= "" data-lazy-src="https://gitee.com/FIF/pic-beg/raw/master/images/vue/Vue.js-i18n-768x512.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-04-12</div><div class="title">（Vue源码一）Vue 源码设计和构建流程</div></div></a></div><div><a href="/blog/2021/04/17/vue/vue-code-4.debugger/" title="（Vue源码二）Vue实例挂载流程"><img class="cover" src= "" data-lazy-src="https://gitee.com/FIF/pic-beg/raw/master/images/vue/1_b2Ig4HR2UOQEV5I2QnQrrw.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-04-17</div><div class="title">（Vue源码二）Vue实例挂载流程</div></div></a></div><div><a href="/blog/2021/04/30/vue/vue-code-7-compiler/" title="（Vue源码五）Vue 编译原理"><img class="cover" src= "" data-lazy-src="https://gitee.com/FIF/pic-beg/raw/master/images/vue/vuetemplate.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-04-30</div><div class="title">（Vue源码五）Vue 编译原理</div></div></a></div><div><a href="/blog/2021/05/04/vue/vue-code-8.component/" title="（Vue源码六）Vue 组件原理分析"><img class="cover" src= "" data-lazy-src="https://gitee.com/FIF/pic-beg/raw/master/images/vue/components.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-05-04</div><div class="title">（Vue源码六）Vue 组件原理分析</div></div></a></div><div><a href="/blog/2021/04/27/vue/vue-code-6.reactive/" title="（Vue源码四）数据响应式原理"><img class="cover" src= "" data-lazy-src="https://gitee.com/FIF/pic-beg/raw/master/images/vue/mvvm.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-04-27</div><div class="title">（Vue源码四）数据响应式原理</div></div></a></div><div><a href="/blog/2021/05/14/vue/vue-code-9.event/" title="（Vue源码七）Vue 事件"><img class="cover" src= "" data-lazy-src="https://gitee.com/FIF/pic-beg/raw/master/images/vue/components.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2021-05-14</div><div class="title">（Vue源码七）Vue 事件</div></div></a></div></div></div><hr/><div id="post-comment"><div class="comment-head"><div class="comment-headline"><i class="fas fa-comments fa-fw"></i><span> 评论</span></div></div><div class="comment-wrap"><div><div class="vcomment" id="vcomment"></div></div></div></div></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="card-info-avatar is-center"><img class="avatar-img" src= "" data-lazy-src="/blog/img/home/avatar.jpeg" onerror="this.onerror=null;this.src='/blog/img/friend_404.gif'" alt="avatar"/><div class="author-info__name">志非</div><div class="author-info__description">自己变得更好，生活才会更美好</div></div><div class="card-info-data"><div class="card-info-data-item is-center"><a href="/blog/archives/"><div class="headline">文章</div><div class="length-num">12</div></a></div><div class="card-info-data-item is-center"><a href="/blog/tags/"><div class="headline">标签</div><div class="length-num">6</div></a></div><div class="card-info-data-item is-center"><a href="/blog/categories/"><div class="headline">分类</div><div class="length-num">7</div></a></div></div><a class="button--animated" id="card-info-btn" target="_blank" rel="noopener" href="https://github.com/JefferyXZF"><i class="fab fa-github"></i><span>Follow Me</span></a><div class="card-info-social-icons is-center"><a class="social-icon" href="https://github.com/JefferyXZF" target="_blank" title="Github"><i class="fab fa-github"></i></a><a class="social-icon" href="https://www.yuque.com/jeffery-kkrpn/uz24n9" target="_blank" title="语雀"><i class="fa fa-folder-open"></i></a><a class="social-icon" href="https://mail.qq.com/" target="_blank" title="Email"><i class="fas fa-envelope"></i></a></div></div><div class="card-widget card-announcement"><div class="item-headline"><i class="fas fa-bullhorn card-announcement-animation"></i><span>公告</span></div><div class="announcement_content">越努力越幸运</div></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%89%8D%E8%A8%80"><span class="toc-number">1.</span> <span class="toc-text">前言</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%80%89%E9%A1%B9%E5%90%88%E5%B9%B6%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF"><span class="toc-number">2.</span> <span class="toc-text">选项合并使用场景</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#init-%E5%88%9D%E5%A7%8B%E5%8C%96%E5%90%88%E5%B9%B6%E9%80%89%E9%A1%B9"><span class="toc-number">2.1.</span> <span class="toc-text">_init 初始化合并选项</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#resolveConstructorOptions-%E9%80%89%E9%A1%B9%E5%90%88%E5%B9%B6"><span class="toc-number">2.2.</span> <span class="toc-text">resolveConstructorOptions 选项合并</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Vue-extend-%E6%B3%A8%E5%86%8C%E7%BB%84%E4%BB%B6%EF%BC%8C%E9%80%89%E9%A1%B9%E5%90%88%E5%B9%B6"><span class="toc-number">2.3.</span> <span class="toc-text">Vue.extend 注册组件，选项合并</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Vue-mixin-%E9%80%89%E9%A1%B9%E5%90%88%E5%B9%B6"><span class="toc-number">2.4.</span> <span class="toc-text">Vue.mixin 选项合并</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#mergeOptions-%E9%80%89%E9%A1%B9%E5%90%88%E5%B9%B6%E6%B5%81%E7%A8%8B"><span class="toc-number">3.</span> <span class="toc-text">mergeOptions 选项合并流程</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#components-%E8%A7%84%E8%8C%83%E6%A3%80%E9%AA%8C"><span class="toc-number">3.1.</span> <span class="toc-text">components 规范检验</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#props-%E8%A7%84%E8%8C%83"><span class="toc-number">3.2.</span> <span class="toc-text">props 规范</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#inject-%E7%9A%84%E8%A7%84%E8%8C%83"><span class="toc-number">3.3.</span> <span class="toc-text">inject 的规范</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#directives-%E7%9A%84%E8%A7%84%E8%8C%83"><span class="toc-number">3.4.</span> <span class="toc-text">directives 的规范</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Vue-%E7%9A%84-extends-%E5%92%8C-mixins-%E6%89%A9%E5%B1%95"><span class="toc-number">3.5.</span> <span class="toc-text">Vue 的 extends 和 mixins 扩展</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%80%89%E9%A1%B9%E5%90%88%E5%B9%B6%E7%AD%96%E7%95%A5"><span class="toc-number">4.</span> <span class="toc-text">选项合并策略</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#data-%E5%90%88%E5%B9%B6%E7%AD%96%E7%95%A5"><span class="toc-number">4.1.</span> <span class="toc-text">data 合并策略</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E9%92%A9%E5%AD%90%E5%87%BD%E6%95%B0%E5%90%88%E5%B9%B6%E7%AD%96%E7%95%A5"><span class="toc-number">4.2.</span> <span class="toc-text">生命周期钩子函数合并策略</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#components-filters-directives-%E8%B5%84%E6%BA%90%E5%90%88%E5%B9%B6%E7%AD%96%E7%95%A5"><span class="toc-number">4.3.</span> <span class="toc-text">components, filters, directives 资源合并策略</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#watch-%E9%80%89%E9%A1%B9%E5%90%88%E5%B9%B6%E7%AD%96%E7%95%A5"><span class="toc-number">4.4.</span> <span class="toc-text">watch 选项合并策略</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#props%EF%BC%8Cmethods%EF%BC%8Cinject%EF%BC%8Ccomputed-%E5%90%88%E5%B9%B6%E7%AD%96%E7%95%A5"><span class="toc-number">4.5.</span> <span class="toc-text">props，methods，inject，computed 合并策略</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#provide-%E5%90%88%E5%B9%B6%E7%AD%96%E7%95%A5"><span class="toc-number">4.6.</span> <span class="toc-text">provide 合并策略</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#defaultStrat-%E9%BB%98%E8%AE%A4%E5%90%88%E5%B9%B6%E7%AD%96%E7%95%A5"><span class="toc-number">4.7.</span> <span class="toc-text">defaultStrat 默认合并策略</span></a></li></ol></li></ol></div></div><script defer="defer" data-pjax="data-pjax" src="/blog/js/card_clock.js"></script><div class="card-widget card-clock"><div id="clock"><img v-if="clockshow == &quot;false&quot;" alt="" src= "" data-lazy-src="/blog/img/weather/loading.gif" height="120px" width="100%"/><table class="clock" v-if="clockshow"><tbody><tr><td class="clockdate">{{date}}</td><td class="weatherimg"><img id="weatherimg" :src="weatherimg" alt=""/></td><td class="temperature">{{temperature}}</td><td class="humidityimg"><img id="humidityimg" :src="humidityimg" alt=""/></td><td class="humidity">{{humidity}}</td></tr><tr class="time"><td colspan="5">{{time}}</td></tr><tr><td class="usaqi" colspan="1"><span>{{ip}}</span></td><td class="city" colspan="2">{{city}}</td><td class="daylight" colspan="2">{{daylight}}</td></tr></tbody></table></div></div><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>最新文章</span></div><div class="aside-list"><div class="aside-list-item"><a class="thumbnail" href="/blog/2021/05/24/tools/hexo/advanced/" title="Hexo 博客页面性能优化"><img src= "" data-lazy-src="https://gitee.com/FIF/pic-beg/raw/master/images/vue/Hexo-logo.png" onerror="this.onerror=null;this.src='/blog/img/404.jpg'" alt="Hexo 博客页面性能优化"/></a><div class="content"><a class="title" href="/blog/2021/05/24/tools/hexo/advanced/" title="Hexo 博客页面性能优化">Hexo 博客页面性能优化</a><time datetime="2021-05-23T16:00:00.000Z" title="发表于 2021-05-24 00:00:00">2021-05-24</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/blog/2021/05/14/vue/vue-code-9.event/" title="（Vue源码七）Vue 事件"><img src= "" data-lazy-src="https://gitee.com/FIF/pic-beg/raw/master/images/vue/components.png" onerror="this.onerror=null;this.src='/blog/img/404.jpg'" alt="（Vue源码七）Vue 事件"/></a><div class="content"><a class="title" href="/blog/2021/05/14/vue/vue-code-9.event/" title="（Vue源码七）Vue 事件">（Vue源码七）Vue 事件</a><time datetime="2021-05-13T16:00:00.000Z" title="发表于 2021-05-14 00:00:00">2021-05-14</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/blog/2021/05/04/vue/vue-code-8.component/" title="（Vue源码六）Vue 组件原理分析"><img src= "" data-lazy-src="https://gitee.com/FIF/pic-beg/raw/master/images/vue/components.png" onerror="this.onerror=null;this.src='/blog/img/404.jpg'" alt="（Vue源码六）Vue 组件原理分析"/></a><div class="content"><a class="title" href="/blog/2021/05/04/vue/vue-code-8.component/" title="（Vue源码六）Vue 组件原理分析">（Vue源码六）Vue 组件原理分析</a><time datetime="2021-05-03T16:00:00.000Z" title="发表于 2021-05-04 00:00:00">2021-05-04</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/blog/2021/04/30/vue/vue-code-7-compiler/" title="（Vue源码五）Vue 编译原理"><img src= "" data-lazy-src="https://gitee.com/FIF/pic-beg/raw/master/images/vue/vuetemplate.jpg" onerror="this.onerror=null;this.src='/blog/img/404.jpg'" alt="（Vue源码五）Vue 编译原理"/></a><div class="content"><a class="title" href="/blog/2021/04/30/vue/vue-code-7-compiler/" title="（Vue源码五）Vue 编译原理">（Vue源码五）Vue 编译原理</a><time datetime="2021-04-29T16:00:00.000Z" title="发表于 2021-04-30 00:00:00">2021-04-30</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/blog/2021/04/27/vue/vue-code-6.reactive/" title="（Vue源码四）数据响应式原理"><img src= "" data-lazy-src="https://gitee.com/FIF/pic-beg/raw/master/images/vue/mvvm.png" onerror="this.onerror=null;this.src='/blog/img/404.jpg'" alt="（Vue源码四）数据响应式原理"/></a><div class="content"><a class="title" href="/blog/2021/04/27/vue/vue-code-6.reactive/" title="（Vue源码四）数据响应式原理">（Vue源码四）数据响应式原理</a><time datetime="2021-04-26T16:00:00.000Z" title="发表于 2021-04-27 00:00:00">2021-04-27</time></div></div></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2021 By 志非</div><div class="footer_custom_text">Hi, welcome to my <a href="/">blog</a>!</div><div id="workboard"></div><script async="async" src="/blog/js/runtime.js"></script></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="translateLink" type="button" title="简繁转换">繁</button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="chat_btn" type="button" title="rightside.chat_btn"><i class="fas fa-sms"></i></button><a id="to_comment" href="#post-comment" title="直达评论"><i class="fas fa-comments"></i></a><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div id="algolia-search"><div class="search-dialog"><div class="search-dialog__title" id="algolia-search-title">Algolia</div><div id="algolia-input-panel"><div id="algolia-search-input"></div></div><hr/><div id="algolia-search-results"><div id="algolia-hits"></div><div id="algolia-pagination"></div><div id="algolia-stats"></div></div><span class="search-close-button"><i class="fas fa-times"></i></span></div><div id="search-mask"></div></div><div><script src="https://cdn.jsdelivr.net/npm/vue"></script><script src="/blog/js/utils.js"></script><script src="/blog/js/main.js"></script><script src="/blog/js/tw_cn.js"></script><script src="https://cdn.jsdelivr.net/npm/medium-zoom/dist/medium-zoom.min.js"></script><script src="https://cdn.jsdelivr.net/npm/instant.page/instantpage.min.js" type="module"></script><script src="https://cdn.jsdelivr.net/npm/vanilla-lazyload/dist/lazyload.iife.min.js"></script><script src="https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.js"></script><script src="/blog/js/search/algolia.js"></script><script>var preloader = {
  endLoading: () => {
    document.body.style.overflow = 'auto';
    document.getElementById('loading-box').classList.add("loaded")
  },
  initLoading: () => {
    document.body.style.overflow = '';
    document.getElementById('loading-box').classList.remove("loaded")

  }
}
window.addEventListener('load',preloader.endLoading())</script><div class="js-pjax"><script>if (document.getElementsByClassName('mermaid').length) {
  if (window.mermaidJsLoad) mermaid.init()
  else {
    getScript('https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js').then(() => {
      window.mermaidJsLoad = true
      mermaid.initialize({
        theme: 'default',
      })
      false && mermaid.init()
    })
  }
}</script><script>function loadValine () {
  function initValine () {
    const valine = new Valine(Object.assign({
      el: '#vcomment',
      appId: 'JC72d1A7XBwC26rGCKMVn5CS-MdYXbMMI',
      appKey: 'xI6bG7FseICz8ShKdmWVe7N1',
      placeholder: 'Please leave your footprints',
      avatar: 'https://gitee.com/FIF/pic-beg/raw/master/images/vue/1619248532079-avatar_coment.webp',
      meta: 'nick,mail,link'.split(','),
      pageSize: '10',
      lang: 'zh-CN',
      recordIP: false,
      serverURLs: '',
      emojiCDN: '',
      emojiMaps: "",
      enableQQ: true,
      path: window.location.pathname,
      requiredFields: ["nick,mail"],
      visitor: false,
      avatar_cdn: 'https://www.gravatar.com/avatar/'
    }, null))
  }

  if (typeof Valine === 'function') initValine() 
  else getScript('https://cdn.jsdelivr.net/npm/valine/dist/Valine.min.js').then(initValine)
}

if ('Valine' === 'Valine' || !true) {
  if (true) btf.loadComment(document.getElementById('vcomment'),loadValine)
  else setTimeout(loadValine, 0)
} else {
  function loadOtherComment () {
    loadValine()
  }
}</script></div><div class="aplayer no-destroy" data-id="000PeZCQ1i4XVs" data-server="tencent" data-type="artist" data-fixed="true" data-mini="true" data-listFolded="false" data-order="random" data-preload="none" data-autoplay="false" pjax="true" muted></div><script defer="defer" id="ribbon" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-ribbon.min.js" size="150" alpha="0.6" zIndex="-1" mobile="false" data-click="true"></script><script id="canvas_nest" defer="defer" color="0,0,255" opacity="0.7" zIndex="-1" count="99" mobile="false" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-nest.min.js"></script><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/activate-power-mode.min.js"></script><script>POWERMODE.colorful = true;
POWERMODE.shake = true;
POWERMODE.mobile = false;
document.body.addEventListener('input', POWERMODE);
</script><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js"></script><script src="https://cdn.jsdelivr.net/gh/metowolf/MetingJS@1.2/dist/Meting.min.js"></script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div></body></html>